
#ifndef __CMDLNPARSER_H__
#define __CMDLNPARSER_H__

#ifdef __cplusplus

#pragma warning( push )
#pragma warning(disable : 4996)

#include <vector>
#include <string>
#include <map>
#include <algorithm>
using namespace std;

#define CMDLINE_INIT_EXT_VAL(type, var, empty_init)	\
	extern type var; \
	CCmdLineInitVal<type> _cmdlineinitval##var(_T(#var), &var, NULL, empty_init);\
	extern "C" __declspec(dllexport allocate(".cmdln$__i")) CmdLineInitValSegPtr _cmdlineinitvalsegptr##var={CMDLINE_INITVAL_SIG, &_cmdlineinitval##var}

#define CMDLINE_INIT_EXTC_VAL(type, var, empty_init)	\
	extern "C" type var; \
	CCmdLineInitVal<type> _cmdlineinitval##var(_T(#var), &var, NULL, empty_init);\
	extern "C" __declspec(dllexport allocate(".cmdln$__i")) CmdLineInitValSegPtr _cmdlineinitvalsegptr##var={CMDLINE_INITVAL_SIG, &_cmdlineinitval##var}

#define CMDLINE_INIT_VAL(type, var, val, des, empty_init)	\
	type var=val; \
	CCmdLineInitVal<type> _cmdlineinitval##var(_T(#var), &var, NULL, empty_init);\
	extern "C" __declspec(dllexport allocate(".cmdln$__i")) CmdLineInitValSegPtr _cmdlineinitvalsegptr##var={CMDLINE_INITVAL_SIG, &_cmdlineinitval##var}


#define CMDLINE_INIT_EXT_NAME_VAL( name, type, var, des, empty_init)	\
	extern type var; \
	CCmdLineInitVal<type> _cmdlineinitval##var(name, &var, des, empty_init);\
	extern "C" __declspec(dllexport allocate(".cmdln$__i")) volatile CmdLineInitValSegPtr _cmdlineinitvalsegptr##var={CMDLINE_INITVAL_SIG, &_cmdlineinitval##var}

#define CMDLINE_INIT_EXTC_NAME_VAL( name, type, var, des, empty_init)	\
	extern "C" type var; \
	CCmdLineInitVal<type> _cmdlineinitval##var(name, &var, des, empty_init);\
	extern "C" __declspec(dllexport allocate(".cmdln$__i")) CmdLineInitValSegPtr _cmdlineinitvalsegptr##var={CMDLINE_INITVAL_SIG, &_cmdlineinitval##var}

#define CMDLINE_INIT_NAME_VAL(name, type, var, val, des, empty_init)	\
	type var=val; \
	CCmdLineInitVal<type> _cmdlineinitval##var(name, &var, des, empty_init);\
	extern "C" __declspec(dllexport allocate(".cmdln$__i")) CmdLineInitValSegPtr _cmdlineinitvalsegptr##var={CMDLINE_INITVAL_SIG, &_cmdlineinitval##var}

// for CMDLINE_INIT_XXX symbol is not referenced directly, 
//  if __declspec(dllexport) is not specified
// then in release build these symbols maybe "stripped"
// add:
// #pragma comment(linker, "/include:"CMDLINE_INIT_LINK_INCLUDE(func_name))
// to force include this symbol in final program
#define CMDLINE_INIT_LINK_INCLUDE(var) "__cmdlineinitvalsegptr"#var


#pragma section(".cmdln$__a",read,write)	// start
#pragma section(".cmdln$__i",read,write)	// command line init values
#pragma section(".cmdln$__z",read,write)	// end

class CCmdLineInitValBase
{
public:
	CCmdLineInitValBase(const TCHAR* pName, const TCHAR* pDes, BOOL fEmptyInit=FALSE)
		: m_pName(pName)
		, m_pDes(pDes)
		, m_fEmptyInit(fEmptyInit)
	{};

public:
	virtual VOID VarExist()=0;
	virtual VOID InitVal(_tstring& str)=0;
	const TCHAR* GetName(){return m_pName;};
	const TCHAR* GetDescription(){return m_pDes;};
	BOOL IsEmptyInit(){return m_fEmptyInit;};
	virtual VOID GetVarString(_tstring* pstr)=0;
	
	
private:
	const TCHAR* m_pName;
	const TCHAR* m_pDes;
	BOOL m_fEmptyInit;
};

template<class T>
class CCmdLineInitVal
		:public CCmdLineInitValBase
{
public:
	CCmdLineInitVal(const TCHAR* pName, T* pVal, const TCHAR* pDes, BOOL fEmptyInit=FALSE)
		: CCmdLineInitValBase(pName, pDes, fEmptyInit)
		, pVar(pVal)
	{};

public:
	VOID VarExist(){VarExistDef(pVar);};
	VOID InitVal(_tstring& str){String2Var(&str, pVar);};
	VOID GetVarString(_tstring* pstr){Var2String(pVar, pstr);};

private:
	template<class V>
	static VOID VarExistDef(V* pVar){};
	template<>
	static VOID VarExistDef<bool>(bool* pVar){*pVar=true;};
// actualy BOOL is a int
//	template<>
//	static VOID VarExistDef<BOOL>(BOOL* pVar){*pVar=TRUE;};

private:
	T* pVar;
};

struct CmdLineInitValSegPtr
{
#define CMDLINE_INITVAL_SIG   0x56444D43  //"CMDV"
	int iSig;
	CCmdLineInitValBase* pCmdLnInitVar;
	static bool IsValid(CmdLineInitValSegPtr* p)
	{return (CMDLINE_INITVAL_SIG==p->iSig);};
};

BOOL BuildUpCmdLnInitVal(class CCmdLineParser* pCmdParser);
VOID PrintCmdLnInitVarsInfo(class CSmpIO* pIO, const TCHAR* pstrPreFix=NULL);
VOID DumpCmdLnInitVar(class CSmpIO* pIO, const TCHAR* pszVar);


class CCmdLineParser
{
public:
	CCmdLineParser();
	BOOL ParseCmdLine(const TCHAR* pszCmdLine);
	BOOL ParseArg(int argc, const TCHAR** argv);
	VOID Clear(){m_mapOpt.clear(); m_vecPara.clear();};

	DWORD GetParaCnt(){return (DWORD)m_vecPara.size();};
	template<class T>
	BOOL GetPara(DWORD id, T* pt)
	{
		if(id>=m_vecPara.size())
		{
			return FALSE;
		}
		
		if(pt)
		{
			String2Var(&(m_vecPara[id]), pt);
		}
		
		return TRUE;
	}
	

	BOOL GetOption(const TCHAR* pszOpt)
	{
		map<_tstring, _tstring>::iterator it=m_mapOpt.find(pszOpt);
		if(it==m_mapOpt.end())
		{
			return FALSE;
		}
		
		return TRUE;
	}

	template<class T>
	BOOL GetOption(const TCHAR* pszOpt, T* pt)
	{
		map<_tstring, _tstring>::iterator it=m_mapOpt.find(pszOpt);
		if(it==m_mapOpt.end())
		{
			return FALSE;
		}
		
		if( pt && (! it->second.empty()) )
		{
			String2Var(&(it->second), pt);
		}
		
		return TRUE;
	}
	
	template<>
	BOOL GetOption<bool>(const TCHAR* pszOpt, bool* pt)
	{
		map<_tstring, _tstring>::iterator it=m_mapOpt.find(pszOpt);
		if(it==m_mapOpt.end())
		{
			return FALSE;
		}
		
		if( pt )
		{
			if(it->second.empty())
			{
				*pt=true;
			}
			else
			{
				String2Var(&(it->second), pt);
			}
		}
		
		return TRUE;
	}
	
	template<>
	BOOL GetOption<_tstring>(const TCHAR* pszOpt, _tstring* pt)
	{
		map<_tstring, _tstring>::iterator it=m_mapOpt.find(pszOpt);
		if(it==m_mapOpt.end())
		{
			return FALSE;
		}
		
		if( pt )
		{
			if(it->second.empty())
			{
				pt->clear();
			}
			else
			{
				*pt=it->second;
			}
		}
		
		return TRUE;
	}

	DWORD GetOptionCnt(){return (DWORD)m_mapOpt.size();};
	template<class T>
	BOOL GetOption(DWORD dwId, const TCHAR** pcszOpt, T* pt)
	{
		for(map<_tstring, _tstring>::iterator it=m_mapOpt.begin(); it!=m_mapOpt.end(); it++)
		{
			if(0==dwId)
			{
				if(pcszOpt)
				{
					*pcszOpt=it->first.c_str();
				}
				
				if( pt && (! it->second.empty()) )
				{
					String2Var(&(it->second), pt);
				}
	
				return TRUE;
			}
	
			dwId--;
		}
			
		return FALSE;
	}
	
	template<>
	BOOL GetOption<bool>(DWORD dwId, const TCHAR** pcszOpt, bool* pt)
	{
		for(map<_tstring, _tstring>::iterator it=m_mapOpt.begin(); it!=m_mapOpt.end(); it++)
		{
			if(0==dwId)
			{
				if(pcszOpt)
				{
					*pcszOpt=it->first.c_str();
				}
				
				if( pt )
				{
					if(it->second.empty())
					{
						*pt=true;
					}
					else
					{
						String2Var(&(it->second), pt);
					}
				}
				
				return TRUE;
			}
	
			dwId--;
		}
			
		return FALSE;
	}

	VOID SetOptionChar(TCHAR chOptionChar){m_chOptionChar=chOptionChar;};
	TCHAR GetOptionChar(){return m_chOptionChar;};

	VOID SetOptionValueChar(TCHAR chOptionValChar){m_chOptionValueChar=m_chOptionChar;};
	TCHAR GetOptionValueChar(){return m_chOptionValueChar;};

private:
	BOOL ParseCmdLine(const TCHAR* pCmdLine, BOOL fEscapeSpace);
	BOOL FindChar(TCHAR chFind, vector<TCHAR>* pvctChS);

private:
	TCHAR m_chOptionChar;
	TCHAR m_chOptionValueChar;

private:
	map<_tstring, _tstring> m_mapOpt;
	vector<_tstring> m_vecPara;
};


#if (defined(CMDLNPARSER_IMP)|defined(KEN_LIB_IMP))

CCmdLineParser::CCmdLineParser()
{
	m_chOptionChar=_T('/');
	m_chOptionValueChar=_T(':');
}

BOOL CCmdLineParser::ParseCmdLine(const TCHAR* pszCmdLine)
{
	return ParseCmdLine(pszCmdLine, FALSE);
}

BOOL CCmdLineParser::ParseArg(int argc, const TCHAR** argv)
{
	for(int i=0; i<argc; i++)
	{
		if(!ParseCmdLine(argv[i], TRUE))
		{
			return FALSE;
		}
	}

	return TRUE;
}

BOOL CCmdLineParser::ParseCmdLine(const TCHAR* pcCmdLine, BOOL fEscapeSpace)
{
	TCHAR* pOption=NULL;
	TCHAR* pOptionVal=NULL;
	TCHAR* pPara=NULL;

	void* pvCmdLine=malloc((_tcslen(pcCmdLine)+1)*sizeof(TCHAR));
	TCHAR* pCmdLine=(TCHAR*)pvCmdLine;
	_tcscpy(pCmdLine, pcCmdLine);

	BOOL fTerminate=FALSE;

	while(*pCmdLine)
	{
		vector<TCHAR> vectInvalid;
		vector<TCHAR> vectTerminator;
		vectTerminator.push_back(0);

        while (_istspace(*pCmdLine)){pCmdLine++;}       // skip leading whitespace
        
        //
        // find option
        // option cna't contain white space
        if( (!pOptionVal)
        	&& (m_chOptionChar==*pCmdLine) )
        {
       		pCmdLine++;
    		pOption=pCmdLine;
    		
    		vectInvalid.push_back(m_chOptionChar);
    		vectInvalid.push_back(_T('\"'));
    		
    		vectTerminator.push_back(_T('\t'));
    		vectTerminator.push_back(_T(' '));
    		vectTerminator.push_back(m_chOptionValueChar);
        }
        // find option value or para
        else
        {
        	// format: /option:"option value"
        	if(_T('\"')==*pCmdLine)
        	{
	    		vectTerminator.push_back(_T('\"'));

				pCmdLine++;
        		if(pOptionVal)
        		{
        			pOptionVal++;
        		}
        	}
        	// format: /option:option_value
        	else
        	{
	    		vectInvalid.push_back(_T('\"'));

				if(!fEscapeSpace)
				{
		    		vectTerminator.push_back(_T('\t'));
		    		vectTerminator.push_back(_T(' '));
				}
        	}
   			
   			if(!pOptionVal)
   			{
	        	pPara=pCmdLine;
   			}
        }
        
		while(1)
        {
			if(FindChar(*pCmdLine, &vectInvalid))
			{
				SmpIOPrintf(_T("invalid command line:%s!\r\n"), pvCmdLine);
				free(pvCmdLine);
				return FALSE;
			}
			
			if(FindChar(*pCmdLine, &vectTerminator))
        	{
				if(0==*pCmdLine)
				{
					fTerminate=TRUE;
				}

				if(pOptionVal)
				{
					_ASSERT(NULL!=pOption);
        			*pCmdLine=0;
    				m_mapOpt[pOption]=pOptionVal;
    				pOptionVal=NULL;
					pOption=NULL;
					break;
				}
        		else if(pOption)
        		{
    				// search for option value
        			if( (m_chOptionValueChar==*pCmdLine)
						&& (*(pCmdLine+1))
						&& ( (fEscapeSpace) || (!_istspace(*(pCmdLine+1)) ) ) )
        			{
        				*pCmdLine=0;
        				pOptionVal=pCmdLine+1;
        				break;
        			}
					else
					{
    					*pCmdLine=0;
    					m_mapOpt[pOption]=_T("");
    					pOption=NULL;
    					break;
					}
        		}
				else if(pPara)
				{
    				*pCmdLine=0;
					m_vecPara.push_back(pPara);
					pPara=NULL;
					break;
				}
        	}

			pCmdLine++;
        }

		if(fTerminate)
		{
			break;
		}
		pCmdLine++;
	}

	free(pvCmdLine);
	return TRUE;
}

BOOL CCmdLineParser::FindChar(TCHAR chFind, vector<TCHAR>* pvctChS)
{
	vector<TCHAR>::iterator it=find(pvctChS->begin(), pvctChS->end(), chFind);
	if(it==pvctChS->end())
	{
		return FALSE;
	}

	return TRUE;
}


//
//==== CCmdLineInitVal help functions ====
//

#define _CMDLN_SEG_FLAG 0x4C444D43  //CMDL
__declspec(allocate(".cmdln$__a")) int _cmdln_seg_a = _CMDLN_SEG_FLAG;
__declspec(allocate(".cmdln$__z")) int _cmdln_seg_z = _CMDLN_SEG_FLAG;


BOOL BuildUpCmdLnInitVal(CCmdLineParser* pCmdParser)
{
    CmdLineInitValSegPtr* pBgn=NULL;
	CmdLineInitValSegPtr* pEnd=NULL;

    FindEleInSeg(&_cmdln_seg_a, &_cmdln_seg_z, &CmdLineInitValSegPtr::IsValid, &pBgn, &pEnd);

	for(CmdLineInitValSegPtr* pCur=pBgn; pCur!=pEnd; pCur++)
	{
		_tstring strVal;
		if(pCmdParser->GetOption(pCur->pCmdLnInitVar->GetName(), &strVal))
		{
			if(strVal.empty())
			{
				pCur->pCmdLnInitVar->VarExist();
			}
			else
			{
				pCur->pCmdLnInitVar->InitVal(strVal);
			}
		}
		else if(pCur->pCmdLnInitVar->IsEmptyInit())
		{
			pCur->pCmdLnInitVar->InitVal(strVal);
		}
	}
	
	return TRUE;
}

VOID PrintCmdLnInitVarsInfo(CSmpIO* pIO, const TCHAR* pstrPreFix)
{
	CmdLineInitValSegPtr* pBgn=NULL;
	CmdLineInitValSegPtr* pEnd=NULL;
	DWORD dwPrefixLen=0;
	if(pstrPreFix)
	{
		dwPrefixLen=_tcslen(pstrPreFix);
	}

	FindEleInSeg(&_cmdln_seg_a, &_cmdln_seg_z, &CmdLineInitValSegPtr::IsValid, &pBgn, &pEnd);

	for(CmdLineInitValSegPtr* pCur=pBgn; pCur!=pEnd; pCur++)
	{
		pIO->IncDeep();
		_tstring str;
		pCur->pCmdLnInitVar->GetVarString(&str);
		if( (!dwPrefixLen)
			|| (0==_tcsncmp(pCur->pCmdLnInitVar->GetName(), pstrPreFix, dwPrefixLen)) )
		{
			pIO->Printf(_T("/%s:%s -- %s"), pCur->pCmdLnInitVar->GetName(), str.c_str(), pCur->pCmdLnInitVar->GetDescription());
		}
		pIO->DecDeep();
	}
}

VOID DumpCmdLnInitVar(class CSmpIO* pIO, const TCHAR* pszVar)
{
	CmdLineInitValSegPtr* pBgn=NULL;
	CmdLineInitValSegPtr* pEnd=NULL;

	FindEleInSeg(&_cmdln_seg_a, &_cmdln_seg_z, &CmdLineInitValSegPtr::IsValid, &pBgn, &pEnd);

	for(CmdLineInitValSegPtr* pCur=pBgn; pCur!=pEnd; pCur++)
	{
		if(0==_tcscmp(pszVar, pCur->pCmdLnInitVar->GetName()))
		{
			_tstring str;
			pCur->pCmdLnInitVar->GetVarString(&str);
			pIO->Printf(_T("%s:%s\r\n"), pszVar, str.c_str());
			return ;
		}
	}

	pIO->Printf(_T("No such var:%s\r\n"), pszVar);
}


#endif	//#ifdef KEN_LIB_IMP | CMDLNPARSER_IMP

#endif  //#ifdef __cplusplus

#pragma warning( pop ) //#pragma warning(disable : 4996)

#endif	//#ifndef __CMDLNPARSER_H__

